﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects.DataClasses;
using Core.Model;
using Core.UnitOfWork;
using System.Data.Objects;
using System.Data;
using System.Collections;

namespace Core.Repository
{
    public class Repository<TEntity> : IRepository<TEntity>
         where TEntity : class,IKey<int>
    {
        public Repository(IUnitOfWork unitOfWork)
        {
            this.UnitOfWork = unitOfWork;
        }

        private IUnitOfWork unitOfWork;
        public IUnitOfWork UnitOfWork
        {
            get
            {
                return this.unitOfWork;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException();
                this.unitOfWork = value;
            }
        }
        private IObjectSet<TEntity> objectset;
        protected IObjectSet<TEntity> ObjectSet
        {
            get
            {
                if (objectset == null)
                {
                    objectset = UnitOfWork.Context.CreateObjectSet<TEntity>();
                }
                return objectset;
            }
        }

        #region IRepository 成员

        public virtual TEntity Get(object id)
        {
            EntityKey key = new EntityKey();
            key.EntityContainerName = UnitOfWork.Context.DefaultContainerName;
            key.EntitySetName = typeof(TEntity).Name;
            key.EntityKeyValues = new[] { new EntityKeyMember("Id", id) };
            object obj = UnitOfWork.Context.GetObjectByKey(key);

            if (obj == null)
                return null; 
            return obj as TEntity;
        }


        public virtual void Add(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException();

            ObjectSet.AddObject(entity);
        }

        public virtual void Update(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException();

            EntityKey key = new EntityKey();
            key.EntityContainerName = UnitOfWork.Context.DefaultContainerName;
            key.EntitySetName = typeof(TEntity).Name;
            key.EntityKeyValues = new [] { new EntityKeyMember("Id", entity.Id) };

            ObjectStateEntry se;
            UnitOfWork.Context.ObjectStateManager.TryGetObjectStateEntry(key, out se);
            if (se != null)
            {
                if (se.Entity != entity)
                {
                    this.UnitOfWork.Context.ApplyCurrentValues(key.EntitySetName, entity);
                }
            }
            else
            {
                object currentEntityInDb = null;
                if (this.UnitOfWork.Context.TryGetObjectByKey(key, out currentEntityInDb))
                {
                    this.UnitOfWork.Context.ApplyCurrentValues(key.EntitySetName, entity);
                }
                else
                {
                    throw new ObjectNotFoundException(string.Format("{0} not exist in {1}", se.EntityKey.EntityKeyValues[0].ToString(), se.EntitySet.Name));
                }
            }

        }

        public virtual void Delete(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException();

            EntityKey key = new EntityKey();
            key.EntityContainerName = UnitOfWork.Context.DefaultContainerName;
            key.EntitySetName = typeof(TEntity).Name;
            key.EntityKeyValues = new [] { new EntityKeyMember("Id", entity.Id) };

            ObjectStateEntry se;
            UnitOfWork.Context.ObjectStateManager.TryGetObjectStateEntry(key, out se);
            if (se != null)
            {
                ObjectSet.DeleteObject((se.Entity as TEntity));
            }
            else
            {
                object currentEntityInDb = null;
                if (this.UnitOfWork.Context.TryGetObjectByKey(key, out currentEntityInDb))
                {
                    ObjectSet.DeleteObject((TEntity)currentEntityInDb);
                }
                else
                {
                    throw new ObjectNotFoundException(string.Format("{0} not exist in {1}", se.EntityKey.EntityKeyValues[0].ToString(), se.EntitySet.Name));
                }
            }
        }

        public virtual IList<TEntity> Find(Func<TEntity, bool> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            return ObjectSet.Where(expression).ToList();

        }

        public virtual IList<TEntity> Find<Tkey>(Func<TEntity, bool> expression,
            Func<TEntity, Tkey>[] orderby, bool[] isDescending, int pageIndex, int pageCount)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");
            if (orderby == null)
                throw new ArgumentNullException("orderby");

            if (orderby.Length != isDescending.Length)
                throw new ArgumentException("orderby count not equal isDescending count!");

            var q = ObjectSet.Where(expression);

            var orderQ = isDescending[0] ? q.OrderByDescending(orderby[0]) : q.OrderBy(orderby[0]);
            for (int i = 1; i < orderby.Length; i++)
            {
                orderQ = isDescending[i] ? orderQ.ThenByDescending(orderby[i]) : orderQ.ThenBy(orderby[i]);
            }
            return orderQ.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList();
        }

        #endregion
    }
}
